﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace YY.TreeTime
{
    public partial class RulerSlider : UserControl, IMessageFilter
    {
        public RulerSlider()
        {
            InitializeComponent();
            BackColor = Color.Transparent;
            this.Height = 20;
        }

        #region 私有变量
        private Color _LineColor = Color.LightYellow;
        private float _LineHeight =1;
        private Color _ThumbColor = Color.Red;
        private Color _ThumbColor2 = Color.White;

        private bool _CanThumbSlide = false;
        private bool _bMouseTrackingOn = false;

        private bool _bDrawLine = false;
        private bool _bInControl = false;
        private double _StartValue = 0;

        private int _iMousePosition = 1;
        private int _iOldMousePosition = -1;

        private int barMinimum = 0;
        private int barMaximum = 100;
        private int trackerValue = 50;
        private int mouseWheelBarPartitions = 10;

        private bool mouseInThumbRegion = false;
        private bool mouseInRegion = false;

        private Rectangle thumbRect;
        private int thumbSize = 9;

        private Size thumbRoundRectSize = new Size(4, 4);
        private Rectangle thumbHalfRect;
        private Border3DStyle _i3DBorderStyle = Border3DStyle.Etched;

        private string _Key = string.Empty;

        #endregion

        public class HooverValueEventArgs : EventArgs
        {
            public double Value;

            public HooverValueEventArgs(double Value)
                : base()
            {
                this.Value = Value;
            }
        }

        public delegate void HooverValueEvent(object sender, HooverValueEventArgs e);

        public event HooverValueEvent HooverValue;

        [Description("Event fires when the Value property changes")]
        [Category("Action")]
        public event EventHandler ValueChanged;

        [Description("Event fires when the Slider position is changed")]
        [Category("Behavior")]
        public event ScrollEventHandler Scroll;

        public event MouseEventHandler MouseDownOnThumb;

        #region 属性


        [Description("关键字")]
        [Category("RulerSlider")]
        [DefaultValue(typeof(string), "")]
        public string Key
        {
            get { return _Key; }
            set { _Key = value; }
        }

        [Description("滑动线的颜色")]
        [Category("RulerSlider")]
        [DefaultValue(typeof(Color), "LightYellow")]
        public Color LineColor
        {
            get { return _LineColor; }
            set
            {
                _LineColor = value;
                Invalidate();
            }
        }

        [Description("滑块的颜色")]
        [Category("RulerSlider")]
        [DefaultValue(typeof(Color), "Red")]
        public Color ThumbColor
        {
            get { return _ThumbColor; }
            set
            {
                _ThumbColor = value;
                Invalidate();
            }
        }

        [Description("滑块的颜色2")]
        [Category("RulerSlider")]
        [DefaultValue(typeof(Color), "White")]
        public Color ThumbColor2
        {
            get { return _ThumbColor2; }
            set
            {
                _ThumbColor2 = value;
                Invalidate();
            }
        }

        [Description("滑动线的粗度")]
        [Category("RulerSlider")]
        [DefaultValue(typeof(float),"1")]
        public float LineHeight
        {
            get { return _LineHeight; }
            set
            {
                _LineHeight = value;
                Invalidate();
            }
        }

        [Description("是否跟踪鼠标位置")]
        [Category("RulerSlider")]
        [DefaultValue(false)]
        public bool MouseTrackingOn
        {
            get { return _bMouseTrackingOn; }
            set
            {
                if (value == _bMouseTrackingOn) return;

                if (value)
                {
                    if (this.Visible) Application.AddMessageFilter(this);
                }
                else
                {
                    Application.RemoveMessageFilter(this);

                    //TODO
                }

                _bMouseTrackingOn = value;

                Invalidate();
            }
        }

        [Description("滑块是否能够滑动")]
        [Category("RulerSlider")]
        [DefaultValue(false)]
        public bool CanThumbSlide
        {
            get { return _CanThumbSlide; }
            set
            {
                _CanThumbSlide = value;
                Invalidate();
            }
        }

        [Description("Set Slider maximal point")]
        [Category("RulerSlider")]
        [DefaultValue(100)]
        public int Maximum
        {
            get { return barMaximum; }
            set
            {
                if (value > barMinimum)
                {
                    barMaximum = value;
                    if (trackerValue > barMaximum)
                    {
                        trackerValue = barMaximum;
                        if (ValueChanged != null) ValueChanged(this, new EventArgs());
                    }
                    Invalidate();
                }
                else throw new ArgumentOutOfRangeException("Maximal value is lower than minimal one");
            }
        }

        [Description("Set Slider minimal point")]
        [Category("RulerSlider")]
        [DefaultValue(0)]
        public int Minimum
        {
            get { return barMinimum; }
            set
            {
                if (value < barMaximum)
                {
                    barMinimum = value;
                    if (trackerValue < barMinimum)
                    {
                        trackerValue = barMinimum;
                        if (ValueChanged != null) ValueChanged(this, new EventArgs());
                    }
                    Invalidate();
                }
                else throw new ArgumentOutOfRangeException("Minimal value is greather than maximal one");
            }
        }

        [Description("Set Slider value")]
        [Category("RulerSlider")]
        [DefaultValue(50)]
        public int Value
        {
            get { return trackerValue; }
            set
            {
                if (value >= barMinimum & value <= barMaximum)
                {
                    trackerValue = value;
                    if (ValueChanged != null) ValueChanged(this, new EventArgs());
                    Invalidate();
                }
                else throw new ArgumentOutOfRangeException("Value is outside appropriate range (min, max)");
            }
        }

        /// <summary>
        /// Gets or sets the mouse wheel bar partitions.
        /// </summary>
        /// <value>The mouse wheel bar partitions.</value>
        /// <exception cref="T:System.ArgumentOutOfRangeException">exception thrown when value isn't greather than zero</exception>
        [Description("Set to how many parts is bar divided when using mouse wheel")]
        [Category("RulerSlider")]
        [DefaultValue(10)]
        public int MouseWheelBarPartitions
        {
            get { return mouseWheelBarPartitions; }
            set
            {
                if (value > 0)
                    mouseWheelBarPartitions = value;
                else throw new ArgumentOutOfRangeException("MouseWheelBarPartitions has to be greather than zero");
            }
        }

        /// <summary>
        /// Gets or sets the size of the thumb.
        /// </summary>
        /// <value>The size of the thumb.</value>
        /// <exception cref="T:System.ArgumentOutOfRangeException">exception thrown when value is lower than zero or grather than half of appropiate dimension</exception>
        [Description("Set Slider thumb size")]
        [Category("RulerSlider")]
        [DefaultValue(9)]
        public int ThumbSize
        {
            get { return thumbSize; }
            set
            {
                if (value > 0 &
                    value < ClientRectangle.Width)
                    thumbSize = value;
                else
                    throw new ArgumentOutOfRangeException(
                        "TrackSize has to be greather than zero and lower than half of Slider width");
                Invalidate();
            }
        }
     
        #endregion

        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool PreFilterMessage(ref Message m)
        {
            if (!this._bMouseTrackingOn) return false;

            if (m.Msg == (int)Msg.WM_MOUSEMOVE)
            {
                int X = 0;
                int Y = 0;

                // The mouse coordinate are measured in screen coordinates because thats what 
                // Control.MousePosition returns.  The Message,LParam value is not used because
                // it returns the mouse position relative to the control the mouse is over. 
                // Chalk and cheese.

                Point pointScreen = Control.MousePosition;

                // Get the origin of this control in screen coordinates so that later we can 
                // compare it against the mouse point to determine it we've hit this control.

                Point pointClientOrigin = new Point(X, Y);
                pointClientOrigin = PointToScreen(pointClientOrigin);

                _bDrawLine = false;
                _bInControl = false;

                HooverValueEventArgs eHoover = null;

                // Work out whether the mouse is within the Y-axis bounds of a vertital ruler or 
                // within the X-axis bounds of a horizontal ruler

                _bDrawLine = (pointScreen.X >= pointClientOrigin.X) && (pointScreen.X <= pointClientOrigin.X + this.Width);
             

                // If the mouse is in valid position...
                if (_bDrawLine)
                {
                    // ...workout the position of the mouse relative to the 
                    X = pointScreen.X - pointClientOrigin.X;
                    Y = pointScreen.Y - pointClientOrigin.Y;

                    // Determine whether the mouse is within the bounds of the control itself
                    _bInControl = (this.ClientRectangle.Contains(new Point(X, Y)));

                    // Make the relative mouse position available in pixel relative to this control's origin
                    ChangeMousePosition( X);
                    eHoover = new HooverValueEventArgs(CalculateValue(_iMousePosition));

                }
                else
                {
                    ChangeMousePosition(-1);
                    eHoover = new HooverValueEventArgs(_iMousePosition);
                }

                // Paint directly by calling the OnPaint() method.  This way the background is not 
                // hosed by the call to Invalidate() so paining occurs without the hint of a flicker
                PaintEventArgs e = null;
                try
                {
                    e = new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle);
                    OnPaint(e);
                }
                finally
                {
                    e.Graphics.Dispose();
                }

                OnHooverValue(eHoover);  //修改该函数就可以输出响应的标尺数字信息
            }

            if ((m.Msg == (int)Msg.WM_MOUSELEAVE) ||
                (m.Msg == (int)Msg.WM_NCMOUSELEAVE))
            {
                _bDrawLine = false;
                PaintEventArgs paintArgs = null;
                try
                {
                    paintArgs = new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle);
                    this.OnPaint(paintArgs);
                }
                finally
                {
                    paintArgs.Graphics.Dispose();
                }
            }

            return false;  // Whether or not the message is filtered
        }

        private void RemoveMessageFilter()
        {
            try
            {
                if (_bMouseTrackingOn)
                {
                    Application.RemoveMessageFilter(this);
                }
            }
            catch { }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            // Take private resize actions here

            this.Invalidate();
        }

        public override void Refresh()
        {
            base.Refresh();
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            DrawColorSlider(e);
        }

        protected override void OnVisibleChanged(EventArgs e)
        {
            base.OnVisibleChanged(e);

            if (this.Visible)
            {
                if (_bMouseTrackingOn) Application.AddMessageFilter(this);
            }
            else
            {
                if (_bMouseTrackingOn) RemoveMessageFilter();
            }
        }

        protected override void OnHandleDestroyed(EventArgs e)
        {
            base.OnHandleDestroyed(e);
            RemoveMessageFilter();
            _bMouseTrackingOn = false;
        }

        protected override void OnControlRemoved(ControlEventArgs e)
        {
            base.OnControlRemoved(e);
            RemoveMessageFilter();
            _bMouseTrackingOn = false;
        }

   
        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.EnabledChanged"></see> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
        protected override void OnEnabledChanged(EventArgs e)
        {
            base.OnEnabledChanged(e);
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseEnter"></see> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            mouseInRegion = true;
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseLeave"></see> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            mouseInRegion = false;
            mouseInThumbRegion = false;
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"></see> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            mouseInThumbRegion = IsPointInRect(e.Location, thumbRect);
            if (mouseInThumbRegion)
            {
                if (MouseDownOnThumb != null) { MouseDownOnThumb(this, e); }
            }

            if (e.Button == MouseButtons.Left)
            {
                Capture = true;
                if (Scroll != null) Scroll(this, new ScrollEventArgs(ScrollEventType.ThumbTrack, trackerValue));
                if (ValueChanged != null) ValueChanged(this, new EventArgs());
                OnMouseMove(e);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"></see> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            mouseInThumbRegion = IsPointInRect(e.Location, thumbRect);
            if (this._CanThumbSlide & Capture & e.Button == MouseButtons.Left)
            {
                ScrollEventType set = ScrollEventType.ThumbPosition;
                Point pt = e.Location;
                int p =  pt.X;
                int margin = thumbSize >> 1;
                p -= margin;
                float coef = (float)(barMaximum - barMinimum) /
                             (float)
                             (ClientSize.Width  -
                              2 * margin);
                trackerValue = (int)(p * coef + barMinimum);

                if (trackerValue <= barMinimum)
                {
                    trackerValue = barMinimum;
                    set = ScrollEventType.First;
                }
                else if (trackerValue >= barMaximum)
                {
                    trackerValue = barMaximum;
                    set = ScrollEventType.Last;
                }

                if (Scroll != null) Scroll(this, new ScrollEventArgs(set, trackerValue));
                if (ValueChanged != null) ValueChanged(this, new EventArgs());
            }
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"></see> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            Capture = false;
            mouseInThumbRegion = IsPointInRect(e.Location, thumbRect);
            if (Scroll != null) Scroll(this, new ScrollEventArgs(ScrollEventType.EndScroll, trackerValue));
            if (ValueChanged != null) ValueChanged(this, new EventArgs());
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.MouseWheel"></see> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            int v = e.Delta / 120 * (barMaximum - barMinimum) / mouseWheelBarPartitions;
            SetProperValue(Value + v);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.GotFocus"></see> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            Invalidate();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.LostFocus"></see> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            Invalidate();
        }

        protected void OnHooverValue(HooverValueEventArgs e)
        {
            if (HooverValue != null) HooverValue(this, e);
        }

        private double CalculateValue(int iOffset)
        {
            if (iOffset < 0) return 0;

            double nValue =(double)iOffset;
            return nValue + this._StartValue;
        }

        private void ChangeMousePosition(int iNewPosition)
        {
            this._iOldMousePosition = this._iMousePosition;
            this._iMousePosition = iNewPosition;
        }

        /// <summary>
        /// Sets the trackbar value so that it wont exceed allowed range.
        /// </summary>
        /// <param name="val">The value.</param>
        private void SetProperValue(int val)
        {
            if (val < barMinimum) Value = barMinimum;
            else if (val > barMaximum) Value = barMaximum;
            else Value = val;
        }

        private static bool IsPointInRect(Point pt, Rectangle rect)
        {
            if (pt.X > rect.Left & pt.X < rect.Right & pt.Y > rect.Top & pt.Y < rect.Bottom)
                return true;
            else return false;
        }

        private void DrawColorSlider(PaintEventArgs e)
        {
            try
            {
                int iThumbHeight=Convert.ToInt32((thumbSize*1.34));

                int TrackX = (((trackerValue - barMinimum) * (ClientRectangle.Width - thumbSize)) / (barMaximum - barMinimum));
                thumbRect = new Rectangle(TrackX, ClientRectangle.Top + ClientRectangle.Height / 2 - iThumbHeight / 2, thumbSize, iThumbHeight);


                using (Pen barPen = new Pen(this._LineColor, this._LineHeight))
                {
                    e.Graphics.DrawLine(barPen, ClientRectangle.Left, ClientRectangle.Top + ClientRectangle.Height / 2,
                        ClientRectangle.Right, ClientRectangle.Top + ClientRectangle.Height / 2);
                }

                //get thumb shape path 
                GraphicsPath thumbPath;
                thumbPath = CreateRoundRectPath(thumbRect, thumbRoundRectSize);
                thumbHalfRect = thumbRect;
                thumbHalfRect.Height /= 2;
                using (
                    LinearGradientBrush lgbThumb =
                        new LinearGradientBrush(thumbHalfRect, this._ThumbColor, this._ThumbColor2,
                                                LinearGradientMode.Vertical))
                {
                    lgbThumb.WrapMode = WrapMode.TileFlipXY;
                    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    e.Graphics.FillPath(lgbThumb, thumbPath);
                    //draw thumb band
                    Color newThumbPenColor = _ThumbColor;
                    if ((Capture || mouseInThumbRegion))
                        newThumbPenColor = ControlPaint.Dark(newThumbPenColor);

                    using (Pen thumbPen = new Pen(newThumbPenColor))
                    {
                        e.Graphics.DrawPath(thumbPen, thumbPath);
                    }
 
                }

                //RenderTrackLine(e.Graphics);
            }
            catch (Exception Err)
            {
                Console.WriteLine("DrawBackGround Error in " + Name + ":" + Err.Message);
            }
        }

        public static GraphicsPath CreateRoundRectPath(Rectangle rect, Size size)
        {
            GraphicsPath gp = new GraphicsPath();
            gp.AddLine(rect.Left + size.Width / 2, rect.Top, rect.Right - size.Width / 2, rect.Top);
            gp.AddArc(rect.Right - size.Width, rect.Top, size.Width, size.Height, 270, 90);

            gp.AddLine(rect.Right, rect.Top + size.Height / 2, rect.Right, rect.Bottom - size.Width / 2);
            gp.AddArc(rect.Right - size.Width, rect.Bottom - size.Height, size.Width, size.Height, 0, 90);

            gp.AddLine(rect.Right - size.Width / 2, rect.Bottom, rect.Left + size.Width / 2, rect.Bottom);
            gp.AddArc(rect.Left, rect.Bottom - size.Height, size.Width, size.Height, 90, 90);

            gp.AddLine(rect.Left, rect.Bottom - size.Height / 2, rect.Left, rect.Top + size.Height / 2);
            gp.AddArc(rect.Left, rect.Top, size.Width, size.Height, 180, 90);
            return gp;
        }

        public void RenderTrackLine(Graphics g)
        {
            if (_bMouseTrackingOn & _bDrawLine)
            {
                int iOffset = Offset();      
                Line(g, _iMousePosition, iOffset, _iMousePosition, Height - iOffset);
            }
        }

        private int Offset()
        {
            int iOffset = 0;

            switch (this._i3DBorderStyle)
            {
                case Border3DStyle.Flat: iOffset = 0; break;
                case Border3DStyle.Adjust: iOffset = 0; break;
                case Border3DStyle.Sunken: iOffset = 2; break;
                case Border3DStyle.Bump: iOffset = 2; break;
                case Border3DStyle.Etched: iOffset = 2; break;
                case Border3DStyle.Raised: iOffset = 2; break;
                case Border3DStyle.RaisedInner: iOffset = 1; break;
                case Border3DStyle.RaisedOuter: iOffset = 1; break;
                case Border3DStyle.SunkenInner: iOffset = 1; break;
                case Border3DStyle.SunkenOuter: iOffset = 1; break;
                default: iOffset = 0; break;
            }

            return iOffset;
        }

        private void Line(Graphics g, int x1, int y1, int x2, int y2)
        {
            using (SolidBrush brush = new SolidBrush(this.ForeColor))
            {
                using (Pen pen = new Pen(brush))
                {
                    g.DrawLine(pen, x1, y1, x2, y2);
                    pen.Dispose();
                    brush.Dispose();
                }
            }
        }
    }
}